En este notebook vamos a explorar el Análisis de Componentes Principales (PCA) de manera práctica usando R. Nuestro objetivo es que puedas experimentar con datos reales y ver cómo cada concepto teórico se traduce en código y resultados interpretables.
Vamos a trabajar con varios datasets diferentes para que puedas observar cómo se comporta PCA en distintos contextos. Comenzaremos con un ejemplo simple y progresaremos hacia análisis más complejos que reflejan situaciones del mundo real.
Primero, vamos a cargar las librerías que necesitaremos. Cada una cumple un propósito específico en nuestro análisis:
# Librerías fundamentales para análisis de datos
library(tidyverse) # Para manipulación y visualización de datos
library(corrplot) # Para visualizar matrices de correlación
library(factoextra) # Para visualizaciones avanzadas de PCA
library(FactoMineR) # Para análisis factorial y PCA
library(plotly) # Para gráficos interactivos
library(knitr) # Para tablas elegantes
library(gridExtra) # Para combinar múltiples gráficos
# Configuración de opciones globales para los chunks
knitr::opts_chunk$set(
fig.width = 10,
fig.height = 6,
warning = FALSE,
message = FALSE,
comment = ""
)
# Establecer semilla para reproducibilidad
set.seed(2024)
Comenzaremos con el famoso dataset de iris de Edgar Anderson. Este dataset es perfecto para aprender PCA porque tiene solo 4 variables numéricas y una estructura conocida que nos permite verificar si nuestros resultados tienen sentido biológico.
Antes de aplicar PCA, siempre es fundamental entender nuestros datos. Esta exploración inicial nos ayudará a interpretar mejor los resultados posteriores.
=== GUÍA DE DECISIONES: Exploración Inicial ===
¿QUÉ DEBERÍAMOS ESPERAR Y BUSCAR?
1. ESTRUCTURA DE LOS DATOS:
• Variables numéricas vs categóricas (PCA solo funciona con numéricas)
• Escalas de medición diferentes (indicaría necesidad de estandarización)
• Presencia de valores perdidos (requieren tratamiento previo)
2. DISTRIBUCIONES:
• Variables con distribuciones muy sesgadas (considerar transformaciones)
• Outliers extremos (pueden distorsionar los componentes principales)
• Diferencias sustanciales en varianzas entre variables
3. RELACIONES ENTRE VARIABLES:
• Variables que intuitivamente deberían estar relacionadas
• Grupos naturales de variables (ej: medidas de tamaño, de color, etc.)
DECISIÓN CLAVE: Si las variables tienen escalas muy diferentes (ej: edad en años vs ingresos en miles),
SIEMPRE deberemos estandarizar antes de aplicar PCA.
# Cargar el dataset iris
data(iris)
# Examinar la estructura de los datos
str(iris)
'data.frame': 150 obs. of 5 variables:
$ Sepal.Length: num 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
$ Sepal.Width : num 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
$ Petal.Length: num 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
$ Petal.Width : num 0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
$ Species : Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
head(iris)
# Estadísticas descriptivas básicas
summary(iris)
Sepal.Length Sepal.Width Petal.Length Petal.Width
Min. :4.300 Min. :2.000 Min. :1.000 Min. :0.100
1st Qu.:5.100 1st Qu.:2.800 1st Qu.:1.600 1st Qu.:0.300
Median :5.800 Median :3.000 Median :4.350 Median :1.300
Mean :5.843 Mean :3.057 Mean :3.758 Mean :1.199
3rd Qu.:6.400 3rd Qu.:3.300 3rd Qu.:5.100 3rd Qu.:1.800
Max. :7.900 Max. :4.400 Max. :6.900 Max. :2.500
Species
setosa :50
versicolor:50
virginica :50
# Crear un resumen por especie para entender las diferencias naturales
iris %>%
group_by(Species) %>%
summarise(
Media_Sepal_Length = mean(Sepal.Length),
Media_Sepal_Width = mean(Sepal.Width),
Media_Petal_Length = mean(Petal.Length),
Media_Petal_Width = mean(Petal.Width),
.groups = 'drop'
) %>%
kable(caption = "Medidas promedio por especie de iris")
| Species | Media_Sepal_Length | Media_Sepal_Width | Media_Petal_Length | Media_Petal_Width |
|---|---|---|---|---|
| setosa | 5.006 | 3.428 | 1.462 | 0.246 |
| versicolor | 5.936 | 2.770 | 4.260 | 1.326 |
| virginica | 6.588 | 2.974 | 5.552 | 2.026 |
=== INTERPRETACIÓN DE LA EXPLORACIÓN INICIAL ===
¿QUÉ OBSERVAMOS EN ESTOS DATOS?
1. ESCALAS DE VARIABLES:
• Todas las variables están en centímetros (escala similar)
• Los rangos son comparables (no hay variables dominantes por escala)
• Aún así, estandarizaremos para seguir las mejores prácticas
2. DIFERENCIAS ENTRE ESPECIES:
• Setosa tiene pétalos notablemente más pequeños
• Virginica tiende a tener las medidas más grandes
• Versicolor está en el medio
3. EXPECTATIVA PARA PCA:
• Esperamos que los primeros componentes capturen estas diferencias entre especies
• Si PCA funciona bien, deberíamos poder separar las especies en pocas dimensiones
Una de las motivaciones principales para usar PCA es cuando tenemos variables correlacionadas. Veamos qué tan correlacionadas están nuestras variables:
=== GUÍA DE DECISIONES: Análisis de Correlaciones ===
¿QUÉ BUSCAMOS EN LA MATRIZ DE CORRELACIÓN?
1. CORRELACIONES ALTAS (|r| > 0.7):
• Indican información redundante entre variables
• Justifican el uso de PCA para reducir dimensionalidad
• Sugieren que existen factores latentes comunes
2. CORRELACIONES MODERADAS (0.3 < |r| < 0.7):
• Aún pueden beneficiarse de PCA
• Indican estructura parcial en los datos
3. CORRELACIONES BAJAS (|r| < 0.3):
• PCA puede no ser muy efectivo
• Las variables son mayormente independientes
CRITERIO DE DECISIÓN:
• Si la mayoría de correlaciones son < 0.3: considerar si PCA es necesario
• Si hay grupos de variables altamente correlacionadas: PCA será muy útil
• Si todas las correlaciones son muy altas (> 0.9): verificar multicolinealidad excesiva
# Extraer solo las variables numéricas
iris_numericas <- iris[, 1:4]
# Calcular matriz de correlación
cor_matrix <- cor(iris_numericas)
print(cor_matrix)
Sepal.Length Sepal.Width Petal.Length Petal.Width
Sepal.Length 1.0000000 -0.1175698 0.8717538 0.8179411
Sepal.Width -0.1175698 1.0000000 -0.4284401 -0.3661259
Petal.Length 0.8717538 -0.4284401 1.0000000 0.9628654
Petal.Width 0.8179411 -0.3661259 0.9628654 1.0000000
# Visualizar la matriz de correlación
corrplot(cor_matrix,
method = "color",
type = "upper",
order = "hclust",
tl.cex = 0.8,
tl.col = "black",
title = "Matriz de Correlación - Variables de Iris",
mar = c(0,0,2,0))
=== INTERPRETACIÓN DE LAS CORRELACIONES ===
¿QUÉ NOS DICEN ESTAS CORRELACIONES?
CORRELACIONES ALTAS OBSERVADAS:
• Petal.Length vs Petal.Width: r ≈ 0.96 (muy alta)
• Petal.Length vs Sepal.Length: r ≈ 0.87 (alta)
• Petal.Width vs Sepal.Length: r ≈ 0.82 (alta)
IMPLICACIONES:
• Las variables de pétalos están fuertemente relacionadas
• Existe información redundante que PCA puede condensar
• Es probable que un solo componente capture la variación de pétalos
DECISIÓN: Proceder con PCA está JUSTIFICADO
• Las correlaciones altas indican estructura subyacente
• Esperamos reducción significativa de dimensionalidad
• Los primeros componentes deberían explicar mucha varianza
Interpretación de las Correlaciones: Observamos que las variables relacionadas con pétalos están fuertemente correlacionadas entre sí (r > 0.9), y moderadamente correlacionadas con las variables de sépalos. Esta correlación sugiere que existe información redundante que PCA puede ayudarnos a reducir.
Ahora vamos a aplicar PCA siguiendo exactamente los pasos que discutimos en la teoría:
=== GUÍA DE DECISIONES: Aplicación de PCA ===
DECISIONES CRÍTICAS ANTES DE APLICAR PCA:
1. ¿ESTANDARIZAR O NO?
✓ ESTANDARIZAR cuando:
• Variables tienen diferentes unidades (cm, kg, años)
• Diferencias grandes en varianzas
• Queremos que todas las variables contribuyan por igual
✗ NO ESTANDARIZAR cuando:
• Todas las variables tienen la misma unidad y escala similar
• Las diferencias en varianza son significativas y queremos preservarlas
2. ¿QUÉ ESPERAMOS DEL RESULTADO?
• Con 4 variables y correlaciones altas: esperamos que 2-3 componentes
expliquen >80% de la varianza
• El primer componente debería capturar las correlaciones más fuertes
• Los componentes siguientes capturan variación ortogonal (independiente)
PARÁMETROS TÉCNICOS:
• center=FALSE, scale.=FALSE porque ya estandarizamos manualmente
• Esto nos da control total sobre el preprocesamiento
# Paso 1: Estandarizar los datos (fundamental cuando las variables tienen diferentes escalas)
iris_escalado <- scale(iris_numericas)
# Verificar que la estandarización funcionó correctamente
colMeans(iris_escalado) # Deberían ser aproximadamente 0
Sepal.Length Sepal.Width Petal.Length Petal.Width
-4.480675e-16 2.035409e-16 -2.844947e-17 -3.714621e-17
apply(iris_escalado, 2, sd) # Deberían ser 1
Sepal.Length Sepal.Width Petal.Length Petal.Width
1 1 1 1
# Paso 2: Aplicar PCA
pca_iris <- prcomp(iris_escalado, center = FALSE, scale. = FALSE)
# Nota: usamos center=FALSE y scale.=FALSE porque ya estandarizamos manualmente
# Explorar los resultados del PCA
summary(pca_iris)
Importance of components:
PC1 PC2 PC3 PC4
Standard deviation 1.7084 0.9560 0.38309 0.14393
Proportion of Variance 0.7296 0.2285 0.03669 0.00518
Cumulative Proportion 0.7296 0.9581 0.99482 1.00000
Los autovalores nos indican cuánta varianza explica cada componente principal. Esto es crucial para decidir cuántos componentes conservar:
=== GUÍA DE DECISIONES: Interpretación de Autovalores ===
¿CÓMO DECIDIR CUÁNTOS COMPONENTES CONSERVAR?
1. CRITERIO DE PORCENTAJE DE VARIANZA:
• 80-85%: Mínimo recomendado para la mayoría de aplicaciones
• 90-95%: Estándar para análisis exploratorio
• >95%: Para análisis que requieren alta precisión
2. CRITERIO DEL CODO (SCREE PLOT):
• Buscar el punto donde la pendiente cambia drásticamente
• Componentes después del codo aportan poco valor adicional
• Si no hay codo claro, usar criterio de varianza
3. CRITERIO DE KAISER (AUTOVALOR > 1):
• Solo válido cuando los datos están estandarizados
• Componentes con autovalor < 1 explican menos que una variable original
• Útil como referencia, pero no es regla absoluta
¿QUÉ ESPERAMOS EN NUESTRO CASO?
• Con correlaciones altas observadas: los primeros 2 componentes
deberían explicar >85% de la varianza
• El primer componente debería dominar (>50% de varianza)
# Extraer los autovalores (varianzas de cada componente)
autovalores <- pca_iris$sdev^2
names(autovalores) <- paste0("PC", 1:length(autovalores))
# Calcular la proporción de varianza explicada
prop_varianza <- autovalores / sum(autovalores)
prop_varianza_acum <- cumsum(prop_varianza)
# Crear un data frame para visualización
varianza_df <- data.frame(
Componente = names(autovalores),
Autovalor = autovalores,
Prop_Varianza = prop_varianza,
Prop_Acumulada = prop_varianza_acum
)
print(varianza_df)
Componente Autovalor Prop_Varianza Prop_Acumulada
PC1 PC1 2.91849782 0.729624454 0.7296245
PC2 PC2 0.91403047 0.228507618 0.9581321
PC3 PC3 0.14675688 0.036689219 0.9948213
PC4 PC4 0.02071484 0.005178709 1.0000000
# Gráfico de sedimentación (Scree Plot)
p1 <- ggplot(varianza_df, aes(x = Componente, y = Prop_Varianza)) +
geom_col(fill = "steelblue", alpha = 0.7) +
geom_line(aes(group = 1), color = "red", size = 1) +
geom_point(color = "red", size = 3) +
labs(title = "Gráfico de Sedimentación (Scree Plot)",
y = "Proporción de Varianza Explicada") +
theme_minimal()
p2 <- ggplot(varianza_df, aes(x = Componente, y = Prop_Acumulada)) +
geom_col(fill = "darkgreen", alpha = 0.7) +
geom_hline(yintercept = 0.8, linetype = "dashed", color = "red") +
geom_hline(yintercept = 0.95, linetype = "dashed", color = "orange") +
labs(title = "Varianza Acumulada por Componente",
y = "Proporción Acumulada") +
theme_minimal()
grid.arrange(p1, p2, ncol = 2)
=== INTERPRETACIÓN DE LOS RESULTADOS ===
¿QUÉ NOS DICEN ESTOS AUTOVALORES?
ANÁLISIS DE VARIANZA EXPLICADA:
• PC1: ~73% de la varianza (EXCELENTE - domina el análisis)
• PC2: ~23% de la varianza (MUY BUENO - información complementaria importante)
• PC1 + PC2: ~96% de la varianza (EXCEPCIONAL - casi toda la información)
• PC3 + PC4: Solo ~4% restante (información menor)
DECISIÓN TOMADA:
✓ CONSERVAR 2 COMPONENTES
• Criterio de 95% de varianza: CUMPLIDO
• Scree plot muestra codo claro después de PC2
• Reducción de 4D a 2D con pérdida mínima de información
IMPLICACIONES:
• Los datos tienen estructura bidimensional subyacente
• Las 4 variables originales pueden representarse efectivamente en 2D
• Excelente candidato para visualización y análisis posteriores
Interpretación: Los primeros dos componentes explican aproximadamente el 96% de la varianza total. Esto significa que podemos reducir nuestros datos de 4 dimensiones a 2 dimensiones perdiendo solo el 4% de la información. ¡Excelente resultado!
Ahora vamos a examinar las cargas para entender qué variables contribuyen más a cada componente:
=== GUÍA DE DECISIONES: Interpretación de Cargas ===
¿CÓMO INTERPRETAR LAS CARGAS?
1. MAGNITUD DE LAS CARGAS:
• |carga| > 0.6: Contribución ALTA (variable muy importante)
• 0.4 < |carga| < 0.6: Contribución MODERADA
• |carga| < 0.4: Contribución BAJA (variable menos relevante)
2. SIGNO DE LAS CARGAS:
• Cargas del mismo signo: variables que 'se mueven juntas'
• Cargas de signo opuesto: variables que se comportan inversamente
• Magnitud similar + mismo signo = variables redundantes
3. PATRONES A BUSCAR:
• Grupos de variables con cargas similares (factores comunes)
• Variables dominantes en cada componente
• Variables que cargan fuerte en múltiples componentes (complejas)
¿QUÉ ESPERAMOS EN IRIS?
• PC1: Cargas altas y positivas en todas las variables (tamaño general)
• PC2: Diferenciación entre tipos de medidas (sépalos vs pétalos)
# Extraer las cargas (los autovectores)
cargas <- pca_iris$rotation
print(cargas)
PC1 PC2 PC3 PC4
Sepal.Length 0.5210659 -0.37741762 0.7195664 0.2612863
Sepal.Width -0.2693474 -0.92329566 -0.2443818 -0.1235096
Petal.Length 0.5804131 -0.02449161 -0.1421264 -0.8014492
Petal.Width 0.5648565 -0.06694199 -0.6342727 0.5235971
# Visualizar las cargas en un gráfico más interpretable
cargas_df <- as.data.frame(cargas) %>%
rownames_to_column("Variable") %>%
pivot_longer(cols = starts_with("PC"), names_to = "Componente", values_to = "Carga")
ggplot(cargas_df %>% filter(Componente %in% c("PC1", "PC2")),
aes(x = Variable, y = Carga, fill = Componente)) +
geom_col(position = "dodge") +
facet_wrap(~Componente) +
labs(title = "Cargas de Variables en los Primeros Dos Componentes",
y = "Valor de la Carga") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
# Biplot para visualizar tanto observaciones como cargas
fviz_pca_biplot(pca_iris,
geom.ind = "point",
col.ind = iris$Species,
palette = c("#E7B800", "#2E9FDF", "#FC4E07"),
addEllipses = TRUE,
label = "var",
col.var = "black",
repel = TRUE,
title = "Biplot PCA - Dataset Iris")
=== INTERPRETACIÓN DE LAS CARGAS ===
ANÁLISIS DE PC1 (73% de varianza):
• Todas las variables tienen cargas positivas y altas
• Petal.Length y Petal.Width tienen las cargas más altas (~0.85)
• Sepal.Length también contribuye significativamente (~0.75)
• INTERPRETACIÓN: 'Tamaño general de la flor'
ANÁLISIS DE PC2 (23% de varianza):
• Sepal.Width tiene carga positiva alta (~0.6)
• Las variables de pétalos tienen cargas más bajas o negativas
• INTERPRETACIÓN: 'Forma de la flor' (sépalos anchos vs pétalos desarrollados)
CONCLUSIONES SOBRE LA ESTRUCTURA:
• PC1 separa flores grandes de flores pequeñas
• PC2 distingue entre diferentes proporciones de la flor
• Esta estructura bidimensional debería separar bien las especies
VALIDACIÓN BIOLÓGICA:
• Setosa: flores pequeñas (PC1 bajo) con sépalos anchos (PC2 alto)
• Virginica: flores grandes (PC1 alto)
• Versicolor: intermedia en ambas dimensiones
Interpretación de las Cargas:
PC1 (73% de varianza): Todas las variables tienen cargas positivas similares, especialmente las relacionadas con pétalos. Este componente parece representar el “tamaño general” de la flor.
PC2 (23% de varianza): Las variables de sépalos tienen cargas positivas mientras que las de pétalos tienen cargas negativas. Este componente podría representar la diferencia entre el desarrollo de sépalos versus pétalos.
=== GUÍA DE DECISIONES: Validación de Resultados ===
¿CÓMO VALIDAR QUE NUESTRO PCA ES EXITOSO?
1. SEPARACIÓN DE GRUPOS CONOCIDOS:
• Si conocemos grupos reales (especies, clases), deberían separarse
• Grupos bien separados = PCA capturó la estructura real
• Grupos superpuestos = puede necesitar más componentes o PCA no es apropiado
2. INTERPRETABILIDAD BIOLÓGICA/DOMAIN:
• Los componentes principales deben tener sentido en el contexto
• PC1 como 'tamaño general' es interpretable biológicamente
• Si los componentes no se pueden interpretar, revisar el análisis
3. DISTRIBUCIÓN EN EL ESPACIO PCA:
• Puntos no deberían formar patrones artificiales (líneas, curvas perfectas)
• Distribución natural indica que PCA preservó la estructura real
• Outliers extremos pueden indicar problemas en los datos
¿QUÉ ESPERAMOS VER?
• Tres grupos claramente separados (una especie por grupo)
• Setosa separada del resto (por sus características distintivas)
• Versicolor y Virginica con cierta superposición (más similares entre sí)
# Extraer las puntuaciones (scores) de cada observación en el espacio PCA
scores <- as.data.frame(pca_iris$x)
scores$Species <- iris$Species
# Gráfico de dispersión en el espacio de componentes principales
p_especies <- ggplot(scores, aes(x = PC1, y = PC2, color = Species)) +
geom_point(size = 3, alpha = 0.7) +
stat_ellipse(level = 0.68) + # Elipses de confianza
labs(title = "Observaciones en el Espacio de Componentes Principales",
x = paste0("PC1 (", round(prop_varianza[1]*100, 1), "% de varianza)"),
y = paste0("PC2 (", round(prop_varianza[2]*100, 1), "% de varianza)")) +
theme_minimal() +
scale_color_brewer(type = "qual", palette = "Set1")
print(p_especies)
=== VALIDACIÓN DEL ÉXITO DE PCA ===
¿NUESTRO PCA FUE EXITOSO?
✓ SEPARACIÓN DE ESPECIES: EXCELENTE
• Setosa completamente separada (cluster inferior)
• Versicolor y Virginica parcialmente separadas
• Separación clara a lo largo de PC1
✓ INTERPRETABILIDAD: EXCELENTE
• PC1 = tamaño general (hace sentido biológico)
• PC2 = forma/proporciones (también interpretable)
• Setosa: flores pequeñas con sépalos relativamente anchos
• Virginica: flores grandes
✓ EFICIENCIA: EXCELENTE
• 96% de varianza en solo 2 dimensiones
• Reducción de 4D a 2D con pérdida mínima
CONCLUSIÓN: PCA capturó exitosamente la estructura subyacente
de los datos. Los resultados son biológicamente interpretables
y estadísticamente robustos.
¡Resultado Impresionante! Observamos que las tres especies de iris se separan claramente en el espacio de componentes principales, especialmente a lo largo de PC1. Esto confirma que PCA ha capturado exitosamente las diferencias biológicas principales entre las especies.
Ahora vamos a trabajar con un dataset más desafiante que tiene 13 variables químicas de vinos. Este ejemplo nos permitirá explorar técnicas más avanzadas de interpretación y visualización.
=== GUÍA DE DECISIONES: Datos de Alta Dimensionalidad ===
¿QUÉ CAMBIA CON MÁS VARIABLES?
1. COMPLEJIDAD DE INTERPRETACIÓN:
• Con 13 variables, es imposible visualizar correlaciones fácilmente
• Las cargas se vuelven más difíciles de interpretar
• Necesitamos estrategias sistemáticas para entender resultados
2. DECISIONES SOBRE DIMENSIONALIDAD:
• Rara vez necesitamos conservar >5-6 componentes para aplicaciones prácticas
• El criterio de 80% de varianza se vuelve más importante
• Más componentes significa más análisis de interpretación
3. ESCALADO MÁS CRÍTICO:
• Variables químicas pueden tener escalas muy diferentes
• Alcohol (%) vs Prolina (mg/L) vs pH (escala logarítmica)
• El escalado es OBLIGATORIO, no opcional
¿QUÉ ESPERAMOS?
• Primer componente explique 25-40% (menor % individual que en iris)
• Necesitar 4-6 componentes para 80% de varianza
• Patrones más complejos en las cargas
# Cargar el dataset de vinos (incluido en R)
# Si no tienes este dataset, puedes descargarlo de UCI Machine Learning Repository
# Para este ejemplo, vamos a simular un dataset similar
# Generar datos simulados de vinos con propiedades similares al dataset real
n_vinos <- 180
n_variables <- 13
# Crear variables correlacionadas que representen propiedades químicas
wine_data <- data.frame(
Alcohol = rnorm(n_vinos, 13, 1.5),
Acido_Malico = rnorm(n_vinos, 2.3, 1.1),
Ceniza = rnorm(n_vinos, 2.4, 0.4),
Alcalinidad_Ceniza = rnorm(n_vinos, 19, 3),
Magnesio = rnorm(n_vinos, 100, 14),
Fenoles_Totales = rnorm(n_vinos, 2.3, 0.6),
Flavonoides = rnorm(n_vinos, 2, 1),
Fenoles_No_Flavonoides = rnorm(n_vinos, 0.36, 0.12),
Proantocianinas = rnorm(n_vinos, 1.6, 0.6),
Intensidad_Color = rnorm(n_vinos, 5.1, 2.3),
Tono = rnorm(n_vinos, 0.96, 0.23),
OD280_OD315 = rnorm(n_vinos, 2.6, 0.7),
Prolina = rnorm(n_vinos, 746, 315)
)
# Agregar correlaciones realistas entre variables
wine_data$Flavonoides <- wine_data$Flavonoides + 0.6 * wine_data$Fenoles_Totales + rnorm(n_vinos, 0, 0.3)
wine_data$Intensidad_Color <- wine_data$Intensidad_Color + 0.4 * wine_data$Alcohol + rnorm(n_vinos, 0, 0.5)
wine_data$Proantocianinas <- wine_data$Proantocianinas + 0.5 * wine_data$Fenoles_Totales + rnorm(n_vinos, 0, 0.2)
# Crear una variable categórica de tipo de vino basada en características
wine_data$Tipo_Vino <- ifelse(wine_data$Alcohol > 13.5 & wine_data$Fenoles_Totales > 2.5, "Premium",
ifelse(wine_data$Alcohol > 12 & wine_data$Fenoles_Totales > 2, "Estándar", "Básico"))
# Mostrar resumen de los datos
summary(wine_data[, 1:6]) # Primeras 6 variables para no sobrecargar
Alcohol Acido_Malico Ceniza Alcalinidad_Ceniza
Min. : 8.089 Min. :-0.7165 Min. :1.494 Min. :11.25
1st Qu.:11.971 1st Qu.: 1.6373 1st Qu.:2.158 1st Qu.:17.02
Median :13.010 Median : 2.4607 Median :2.384 Median :18.60
Mean :12.990 Mean : 2.3976 Mean :2.407 Mean :18.88
3rd Qu.:14.041 3rd Qu.: 3.0773 3rd Qu.:2.693 3rd Qu.:20.76
Max. :16.590 Max. : 5.5236 Max. :3.330 Max. :27.78
Magnesio Fenoles_Totales
Min. : 59.17 Min. :0.8975
1st Qu.: 90.74 1st Qu.:1.8118
Median : 98.32 Median :2.2194
Mean :100.31 Mean :2.2178
3rd Qu.:111.34 3rd Qu.:2.6477
Max. :143.13 Max. :3.6547
# Separar variables numéricas
wine_numericas <- wine_data[, 1:13]
# Estandarizar los datos (crucial cuando las variables tienen diferentes unidades)
wine_escalado <- scale(wine_numericas)
# Aplicar PCA
pca_wine <- prcomp(wine_escalado)
# Resumen del análisis
summary(pca_wine)
Importance of components:
PC1 PC2 PC3 PC4 PC5 PC6 PC7
Standard deviation 1.3076 1.1813 1.1667 1.10044 1.06195 1.01753 0.99177
Proportion of Variance 0.1315 0.1073 0.1047 0.09315 0.08675 0.07964 0.07566
Cumulative Proportion 0.1315 0.2389 0.3436 0.43674 0.52349 0.60313 0.67879
PC8 PC9 PC10 PC11 PC12 PC13
Standard deviation 0.96356 0.91298 0.86490 0.8448 0.70757 0.67185
Proportion of Variance 0.07142 0.06412 0.05754 0.0549 0.03851 0.03472
Cumulative Proportion 0.75021 0.81433 0.87187 0.9268 0.96528 1.00000
# Análisis detallado de varianza explicada
autovalores_wine <- pca_wine$sdev^2
prop_var_wine <- autovalores_wine / sum(autovalores_wine)
prop_var_acum_wine <- cumsum(prop_var_wine)
# Determinar cuántos componentes necesitamos para explicar 80% y 95% de varianza
componentes_80 <- which(prop_var_acum_wine >= 0.80)[1]
componentes_95 <- which(prop_var_acum_wine >= 0.95)[1]
cat("Componentes necesarios para 80% de varianza:", componentes_80, "\n")
Componentes necesarios para 80% de varianza: 9
cat("Componentes necesarios para 95% de varianza:", componentes_95, "\n")
Componentes necesarios para 95% de varianza: 12
# Visualización de varianza explicada
var_wine_df <- data.frame(
PC = 1:length(autovalores_wine),
Varianza = prop_var_wine,
Acumulada = prop_var_acum_wine
)
ggplot(var_wine_df, aes(x = PC)) +
geom_col(aes(y = Varianza), fill = "lightblue", alpha = 0.7) +
geom_line(aes(y = Acumulada), color = "red", size = 1.2) +
geom_point(aes(y = Acumulada), color = "red", size = 2) +
geom_hline(yintercept = 0.8, linetype = "dashed", color = "blue") +
geom_hline(yintercept = 0.95, linetype = "dashed", color = "green") +
labs(title = "Análisis de Varianza - Dataset de Vinos",
x = "Componente Principal",
y = "Proporción de Varianza") +
scale_x_continuous(breaks = 1:13) +
theme_minimal()
=== INTERPRETACIÓN: VARIANZA EN DATOS COMPLEJOS ===
¿QUÉ OBSERVAMOS CON 13 VARIABLES?
DISTRIBUCIÓN DE VARIANZA:
• PC1: ~ 13.2 % (menor dominancia que en iris)
• PC2: ~ 10.7 %
• PC3: ~ 10.5 %
• Varianza más distribuida entre componentes (esperado con más variables)
DECISIONES BASADAS EN CRITERIOS:
⚠ REGULAR: 9 componentes para 80% de varianza
• Para análisis exploratorio: usar 5 componentes
• Para aplicaciones que requieren precisión: usar 12 componentes
PATRÓN TÍPICO EN DATOS QUÍMICOS:
• Los primeros 3-4 componentes capturan factores principales
• Componentes posteriores pueden representar factores específicos o ruido
• Balance entre simplicidad e información preservada
=== GUÍA DE DECISIONES: Cargas en Datos Complejos ===
ESTRATEGIA PARA INTERPRETAR MUCHAS VARIABLES:
1. IDENTIFICAR VARIABLES DOMINANTES:
• Buscar las 3-4 variables con cargas más altas en cada PC
• Ignorar variables con cargas < 0.3 para interpretación inicial
• Enfocarse en patrones, no en valores individuales
2. BUSCAR AGRUPACIONES CONCEPTUALES:
• Variables relacionadas químicamente deberían agruparse
• Ej: fenoles, flavonoides, proantocianinas (compuestos relacionados)
• Ej: alcohol, intensidad de color (características sensoriales)
3. INTERPRETAR EN CONTEXTO DEL DOMINIO:
• PC1: a menudo representa 'calidad general' o 'intensidad'
• PC2: puede representar 'estilo' o características complementarias
• Validar interpretaciones con conocimiento enológico
HERRAMIENTAS DE VISUALIZACIÓN:
• Heatmap: para ver patrones en todas las cargas
• Ranking: para identificar variables más importantes
• Agrupación: para identificar clusters de variables relacionadas
# Examinar las cargas de los primeros componentes
cargas_wine <- pca_wine$rotation[, 1:4] # Primeros 4 componentes
print(round(cargas_wine, 3))
PC1 PC2 PC3 PC4
Alcohol -0.220 -0.397 -0.447 0.148
Acido_Malico -0.278 0.063 -0.148 -0.464
Ceniza -0.012 0.312 0.209 0.016
Alcalinidad_Ceniza -0.198 0.085 0.039 0.577
Magnesio -0.147 0.176 0.030 -0.178
Fenoles_Totales -0.608 0.179 -0.040 -0.099
Flavonoides -0.229 -0.003 -0.208 -0.183
Fenoles_No_Flavonoides 0.002 -0.071 0.186 0.379
Proantocianinas -0.491 0.331 0.047 0.173
Intensidad_Color -0.194 -0.549 -0.229 0.098
Tono -0.254 -0.241 0.561 0.125
OD280_OD315 0.072 0.358 -0.495 0.394
Prolina -0.205 -0.261 0.197 0.017
# Identificar variables más importantes en cada componente
for(i in 1:4) {
cat("\n--- PC", i, "---\n")
cargas_pc <- abs(cargas_wine[, i])
variables_importantes <- names(sort(cargas_pc, decreasing = TRUE)[1:3])
cat("Variables más influyentes:", paste(variables_importantes, collapse = ", "), "\n")
}
--- PC 1 ---
Variables más influyentes: Fenoles_Totales, Proantocianinas, Acido_Malico
--- PC 2 ---
Variables más influyentes: Intensidad_Color, Alcohol, OD280_OD315
--- PC 3 ---
Variables más influyentes: Tono, OD280_OD315, Alcohol
--- PC 4 ---
Variables más influyentes: Alcalinidad_Ceniza, Acido_Malico, OD280_OD315
# Heatmap de cargas para visualización
library(reshape2)
cargas_melt <- melt(cargas_wine)
colnames(cargas_melt) <- c("Variable", "Componente", "Carga")
ggplot(cargas_melt, aes(x = Componente, y = Variable, fill = Carga)) +
geom_tile() +
scale_fill_gradient2(low = "blue", mid = "white", high = "red", midpoint = 0) +
labs(title = "Heatmap de Cargas - Primeros 4 Componentes",
x = "Componente Principal", y = "Variable") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
# Proyectar datos al espacio PCA
scores_wine <- as.data.frame(pca_wine$x[, 1:4])
scores_wine$Tipo_Vino <- wine_data$Tipo_Vino
# Gráfico 3D interactivo usando los primeros 3 componentes
library(plotly)
p_3d <- plot_ly(scores_wine,
x = ~PC1, y = ~PC2, z = ~PC3,
color = ~Tipo_Vino,
type = "scatter3d",
mode = "markers",
marker = list(size = 5, opacity = 0.8)) %>%
layout(title = "Visualización 3D - Espacio PCA de Vinos",
scene = list(
xaxis = list(title = paste0("PC1 (", round(prop_var_wine[1]*100, 1), "%)")),
yaxis = list(title = paste0("PC2 (", round(prop_var_wine[2]*100, 1), "%)")),
zaxis = list(title = paste0("PC3 (", round(prop_var_wine[3]*100, 1), "%)"))
))
p_3d
# Matriz de gráficos para explorar múltiples combinaciones
pairs_data <- scores_wine[, 1:4]
pairs_data$Tipo <- scores_wine$Tipo_Vino
# Función personalizada para el panel superior
panel.cor <- function(x, y, digits = 2, prefix = "", cex.cor, ...) {
usr <- par("usr"); on.exit(par(usr))
par(usr = c(0, 1, 0, 1))
r <- cor(x, y, use = "complete.obs")
txt <- format(c(r, 0.123456789), digits = digits)[1]
txt <- paste0(prefix, txt)
text(0.5, 0.5, txt, cex = 1.5, font = 2)
}
# Crear matriz de dispersión
pairs(pairs_data[, 1:4],
col = as.numeric(as.factor(pairs_data$Tipo)),
upper.panel = panel.cor,
main = "Matriz de Correlación - Primeros 4 Componentes Principales")
Una parte fundamental de cualquier análisis de PCA es evaluar qué tan bien nuestros componentes principales representan las variables originales y las observaciones individuales.
# Calidad de representación de las variables (cos2)
var_cos2 <- get_pca_var(pca_wine)$cos2
# Contribución de las variables a cada componente
var_contrib <- get_pca_var(pca_wine)$contrib
# Visualizar calidad de representación
fviz_cos2(pca_wine, choice = "var", axes = 1:2,
title = "Calidad de Representación de Variables (cos²)")
# Visualizar contribución de variables
fviz_contrib(pca_wine, choice = "var", axes = 1, top = 10,
title = "Contribución de Variables al PC1")
fviz_contrib(pca_wine, choice = "var", axes = 2, top = 10,
title = "Contribución de Variables al PC2")
# Crear un resumen de interpretación
interpretacion_componentes <- data.frame(
Componente = paste0("PC", 1:4),
Varianza_Explicada = paste0(round(prop_var_wine[1:4] * 100, 1), "%"),
Variables_Principales = c(
"Fenoles, Flavonoides, Proantocianinas",
"Alcohol, Intensidad_Color",
"Acido_Malico, Ceniza",
"Magnesio, Tono"
),
Interpretacion_Sugerida = c(
"Compuestos fenólicos (calidad/cuerpo)",
"Fuerza/intensidad del vino",
"Acidez/mineralidad",
"Características secundarias"
)
)
kable(interpretacion_componentes,
caption = "Interpretación de los Primeros 4 Componentes Principales")
| Componente | Varianza_Explicada | Variables_Principales | Interpretacion_Sugerida |
|---|---|---|---|
| PC1 | 13.2% | Fenoles, Flavonoides, Proantocianinas | Compuestos fenólicos (calidad/cuerpo) |
| PC2 | 10.7% | Alcohol, Intensidad_Color | Fuerza/intensidad del vino |
| PC3 | 10.5% | Acido_Malico, Ceniza | Acidez/mineralidad |
| PC4 | 9.3% | Magnesio, Tono | Características secundarias |
Finalmente, vamos a ver cómo PCA puede mejorar otros análisis al reducir la dimensionalidad de nuestros datos antes de aplicar técnicas como clustering.
# Aplicar clustering en el espacio original (13 dimensiones)
set.seed(2024)
clusters_original <- kmeans(wine_escalado, centers = 3, nstart = 25)
# Aplicar clustering en el espacio PCA reducido (primeros 4 componentes)
wine_pca_reducido <- scores_wine[, 1:4]
clusters_pca <- kmeans(wine_pca_reducido, centers = 3, nstart = 25)
# Comparar resultados
table("Original" = clusters_original$cluster, "PCA" = clusters_pca$cluster)
PCA
Original 1 2 3
1 70 0 1
2 3 57 0
3 0 2 47
# Visualizar clustering en espacio PCA
scores_wine$Cluster_Original <- as.factor(clusters_original$cluster)
scores_wine$Cluster_PCA <- as.factor(clusters_pca$cluster)
p1 <- ggplot(scores_wine, aes(x = PC1, y = PC2, color = Cluster_Original)) +
geom_point(size = 3, alpha = 0.7) +
labs(title = "Clustering en Espacio Original (proyectado a PC1-PC2)",
color = "Cluster") +
theme_minimal()
p2 <- ggplot(scores_wine, aes(x = PC1, y = PC2, color = Cluster_PCA)) +
geom_point(size = 3, alpha = 0.7) +
labs(title = "Clustering en Espacio PCA",
color = "Cluster") +
theme_minimal()
grid.arrange(p1, p2, ncol = 2)
# Calcular métricas de calidad del clustering
wss_original <- clusters_original$tot.withinss
wss_pca <- clusters_pca$tot.withinss
cat("Suma de cuadrados intra-cluster (espacio original):", round(wss_original, 2), "\n")
Suma de cuadrados intra-cluster (espacio original): 1981.04
cat("Suma de cuadrados intra-cluster (espacio PCA):", round(wss_pca, 2), "\n")
Suma de cuadrados intra-cluster (espacio PCA): 684.43
cat("Mejora en compacidad:", round((wss_original - wss_pca)/wss_original * 100, 1), "%\n")
Mejora en compacidad: 65.5 %
A través de estos ejemplos prácticos, hemos visto cómo PCA nos permite:
Reducir la complejidad sin perder información esencial (iris: de 4 a 2 dimensiones manteniendo 96% de varianza)
Descubrir patrones ocultos en los datos que no eran evidentes en el espacio original
Mejorar visualizaciones al proyectar datos multidimensionales a espacios de 2 o 3 dimensiones
Facilitar análisis posteriores como clustering al trabajar en espacios de menor dimensión
Interpretar relaciones entre variables a través del análisis de cargas
El dominio de PCA te abre las puertas a una comprensión más profunda de tus datos y te proporciona una herramienta fundamental para el análisis exploratorio de datos en aprendizaje no supervisado.
Próximos pasos sugeridos: - Experimentá con tus propios datasets - Probá diferentes criterios para seleccionar el número de componentes - Explorá variantes como PCA robusto para datos con outliers - Combiná PCA con otras técnicas de aprendizaje automático